package stardust.product.platform.webc;


import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import org.springframework.context.ApplicationContext;
import stardust.core.java.DynamicClassLoader;
import stardust.core.java.reflect.AnnotationReflect;
import stardust.core.java.reflect.ReflectUtil;
import stardust.core.java.reflect.scaner.Resource;
import stardust.core.java.reflect.scaner.ResourceScaner;
import stardust.core.java.reflect.scaner.ScanFilter;
import stardust.core.network.protocol.http.support.URI;
import stardust.product.platform.webc.annotation.Handler;
import stardust.product.platform.webc.annotation.Parameter;
import stardust.product.platform.webc.annotation.Renderer;
import stardust.product.platform.webc.container.ApplicationContextContainer;
import stardust.product.platform.webc.container.WebcContainer;
import stardust.product.platform.webc.log.LogController;
import stardust.product.platform.webc.log.LogFilter;
import stardust.product.platform.webc.spi.servlet.JsonRenderer;
import stardust.product.platform.webc.spi.servlet.JspRenderer;
import stardust.product.platform.webc.spi.servlet.TextRenderer;
import stardust.product.platform.webc.spi.servlet.VelocityRenderer;
import stardust.product.platform.webc.spi.servlet.WebcServlet;
import stardust.product.platform.webc.support.WebcContextUtils;


/**
 * Webc上下文类，通过URI实现复合资源访问服务。 <br/> 依据WebcConfig类初始化Webc容器和进行程序包扫描，缓存所有拥有{@link Handler}标注实现的方法类，并实现应用级上下文管理。 <br/>
 * 实现通过唯一URI访问资源，并依据配置或{@link RenderHandle}标注调用相应的响应渲染器输出。
 * <p/>
 *
 * @see WebcConfig
 * @see WebcRequestHandler
 * @see WebcException
 * @see Handler
 * @see Parameter
 * @see Renderer
 * @see RenderHandle
 * @author ChenChang
 */
public class WebcContext
{

  public static final Logger log = Logger.getLogger(WebcContext.class.getName());

  /**
   * 动态类加载器，用于从资源扫描器探测到class文件后把类加载到虚拟机。
   */
  private DynamicClassLoader dynamicClassLoader;

  /**
   * Webc配置类，描述Webc上下文的一些配置信息以及工作方式。
   */
  protected WebcConfig config;

  /**
   * Webc容器类，缓存所有入口点类，以及查找派遣和响应渲染器。
   */
  protected WebcContainer container;

  /**
   * 通过Webc配置加载派遣容器，默认容器通过使用{@link java.lang.Class#newInstance()}实现。 <br/> 支持通过Spring容器管理，通过{@link ApplicationContext#getBean()}
   * 实现，如果资源类未在Spring中定义会自动注册后再获取。 <br/> 容器初始化后使用{@link ResourceScaner}资源扫描器和{@link DynamicClassLoader}
   * 动态类加载器进行程序包扫描，查找出所有拥有{@link Handler}标注实现的类。 <br/> <font color="red">警告：如果程序包过多或者程序包类里的类数量太多可能会导致堆栈溢出，大部分JVM无法释放这部分内存空间。 </font>
   * <p/>
   *
   * @param config 一个{@link WebcConfig}对象。
   */
  public WebcContext(WebcConfig config)
  {
    this.config = config;

    long beginTime = System.currentTimeMillis();

    try
    {
      WebcContext.log.info("Initializing Webc...");

      this.dynamicClassLoader = new DynamicClassLoader();

      // 初始化派遣容器。
      this.initializeContainer();

      // 初始化回调程序。
      this.initializeCallback();

      // 初始化响应渲染器。
      this.initializeRenderer();

      // 初始化过滤器。
      this.initializeFilter(this.config.getFilterClass().toArray(new String[0]));

      // 初始化动态类加载器并依照Webc配置文件扫描程序包，缓存符合条件的类实例。
      this.initializeHandler(this.config.getBasePackage().toArray(new String[0]));

      // 在应用中注册Webc上下文。
      synchronized (WebcContextUtils.DEFAULT_CONTEXT)
      {
        WebcContextUtils.setContext(this.config.getContextName(), this);
      }

      WebcContext.log.info("Webc initialized in " + (System.currentTimeMillis() - beginTime) + " ms.");
    }
    catch (Exception ex)
    {
      throw new WebcException("初始化WebcContext上下文失败。", ex);
    }
  }

  /**
   * 检查是否存在URI指定的资源。
   * <p/>
   *
   * @param uri 资源URI，是一个<tt>String</tt>类型。
   * @return 如果存在资源URI对应的资源处理器则返回<tt>true</tt>值，否则返回<tt>false</tt>值。
   */
  public boolean hasInvoker(String uri)
  {
    return this.container.getHandler(uri) != null;
  }

  /**
   * 调用过滤器队列，检测是否要放弃调用。
   */
  protected boolean doBeforeFilter(String uri, WebcRequestHandler request, WebcResourceReference reference)
  {
    // 如果是内嵌对象将忽略用户过滤器的控制。
    boolean isInnerFilter = WebcContext.isInnerObject(reference.getInstance());
    
    for (int i = 0; i < this.container.getFilterQueue().size(); i++)
    {
      if (!this.container.getFilterQueue().get(i).doBeforeFilter(uri, request, reference))
      {
        if (!isInnerFilter)
        {
          return false;
        }
      }
    }

    return true;
  }

  protected void doAfterFilter(String uri, WebcRequestHandler request, WebcResourceReference reference, Object returnValue, Throwable exception)
  {
    for (int i = this.container.getFilterQueue().size() - 1; i >= 0; i--)
    {
      this.container.getFilterQueue().get(i).doAfterFilter(uri, request, reference, returnValue, exception);
    }
  }

  /**
   * 通过指定URI执行资源处理程序并输出响应。
   * <p/>
   *
   * @param uri 资源URI地址。
   * @param request 处理Webc请求的接口。
   * <p/>
   * @return 资源请求结果。
   * @throws Throwable
   */
  public Object invoke(String uri, WebcRequestHandler request) throws Throwable
  {
    WebcResourceReference reference;
    Object returnValue;
    RenderHandle renderer;

    reference = this.container.getHandler(uri);
    if (reference == null)
    {
      throw new WebcException("无法访问资源[" + uri + "]，没有找到资源处理程序。");
    }

    if (reference.getEntryResult() == null)
    {
      renderer = request.getDefaultRenderer();
    }
    else
    {
      renderer = this.container.getRenderer(reference.getEntryResult().value());
    }

    if (renderer == null)
    {
      if (reference.getEntryResult() != null)
      {
        throw new WebcException("无法访问资源[" + uri + " -> " + reference.getUri() + "]，没有找到资源处理程序指定的响应渲染器[" + reference.getEntryResult().value() + "]。");
      }
      else
      {
        throw new WebcException("无法访问资源[" + uri + " -> " + reference.getUri() + "]，资源处理程序没有指定响应渲染器并且请求源未能提供默认渲染器。");
      }
    }

    // 调用过滤器。
    if (!this.doBeforeFilter(uri, request, reference))
    {
      return null;
    }

    try
    {
      // 处理请求。
      returnValue = request.process(uri, reference);
      // 解析结果。
      RenderForward forward;

      if (returnValue instanceof RenderForward)
      {
        forward = (RenderForward) returnValue;
      }
      else
      {
        forward = new RenderForward(reference.getEntryPoint().resultPath(), returnValue);
      }

      renderer.render(request, reference, uri, forward);
      this.doAfterFilter(uri, request, reference, returnValue, null);
    }
    catch (Throwable e)
    {
      this.doAfterFilter(uri, request, reference, null, e);
      throw e;
    }

    return returnValue;
  }

  /**
   * 通过指定URI执行资源处理程序但不转换输出结果。
   * <p/>
   *
   * @param uri 资源URI地址。
   * @param request 处理Webc请求的接口。
   * <p/>
   * @return 资源请求结果。
   * @throws Throwable
   */
  public Object invokeWithNoRender(String uri, WebcRequestHandler request) throws Throwable
  {
    WebcResourceReference reference;
    Object returnValue;

    reference = this.container.getHandler(uri);
    if (reference == null)
    {
      throw new WebcException("无法访问资源[" + uri + "]，没有找到入口点。");
    }

    // 调用过滤器。
    if (!this.doBeforeFilter(uri, request, reference))
    {
      return null;
    }

    try
    {
      // 处理请求。
      returnValue = request.process(uri, reference);
      returnValue = returnValue instanceof RenderForward ? ((RenderForward) returnValue).getResult() : returnValue;
      this.doAfterFilter(uri, request, reference, returnValue, null);
    }
    catch (Throwable e)
    {
      this.doAfterFilter(uri, request, reference, null, e);
      throw e;
    }

    return returnValue;
  }

  protected boolean registerClass(Class<?> clazz)
  {
    boolean flag = false;
    Method[] method = clazz.getMethods();
    URI uri;
    WebcResourceReference reference;

    m:
    for (Method m : method)
    {
      Handler handler = AnnotationReflect.getAnnotation(m, Handler.class);

      if (handler != null)
      {
        flag = true;
        uri = new URI(handler.value());
        reference = new WebcResourceReference(uri.getURIString(), clazz, m);

        // 通知回调程序，发现新的资源处理程序即将注册。
        if (this.getContainer().getCallback() != null && !this.getContainer().getCallback().isEmpty())
        {
          for (int i = 0; i < this.getContainer().getCallback().size(); i++)
          {
            if (!this.getContainer().getCallback().get(i).onBeforeRegisterHandler(this, reference))
            {
              continue m;
            }
          }
        }
        
        System.out.println("" + this.container.getHandlerCount() + ".\t" + reference.getUri() + "\n\t" + reference.getTargetMethod().toGenericString() + "\n");
        this.container.registerHandler(reference.getUri(), reference);

        // 通知回调程序，已经注册了一个资源处理程序。
        if (this.getContainer().getCallback() != null && !this.getContainer().getCallback().isEmpty())
        {
          for (int i = 0; i < this.getContainer().getCallback().size(); i++)
          {
            if (!this.getContainer().getCallback().get(i).onAfterRegisterHandler(this, reference))
            {
              this.getContainer().destroyHandler(reference.getUri());
              continue m;
            }
          }
        }
      }
    }

    return flag;
  }

  private void initializeContainer()
  {
    /*
     * 通过Webc配置文件加载资源容器。
     */
    if (this.config.getContainerType() == null || this.config.getContainerType().equals("new"))
    {
      this.container = new WebcContainer(this.config);
    }
    else if (this.config.getContainerType().equals("spring"))
    {
      this.container = new ApplicationContextContainer(this.config);
    }
  }

  private void initializeCallback() throws ClassNotFoundException
  {
    /*
     * 通过Webc回调程序，回调程序用于事件通知、修改设置。
     */
    if (this.config.getCallback() != null && !this.config.getCallback().isEmpty())
    {
      for (int i = 0; i < this.config.getCallback().size(); i++)
      {
        this.container.registerCallback((Class<WebcCallback>) this.dynamicClassLoader.loadClass(this.config.getCallback().get(i)));
      }
    }
  }

  private void initializeRenderer() throws InstantiationException, IllegalAccessException, ClassNotFoundException
  {
    /*
     * 向容器注册配置的响应渲染器。
     */
    Iterator<String> it = this.config.getRenderer().keySet().iterator();
    String is;
    while (it.hasNext())
    {
      is = it.next();
      this.container.registerRenderer(is, (RenderHandle) this.dynamicClassLoader.loadClass(this.config.getRenderer().get(is)).newInstance());
    }

    // 注册空白渲染器。
    this.container.registerRenderer(Renderer.NONE, new RenderHandle()
    {
      public void render(WebcRequestHandler request, WebcResourceReference reference, String uri, RenderForward forward)
      {
        return;
      }
    });
    
    /*
     * 注册内置渲染器。
     */
    this.getContainer().registerRenderer(Renderer.JSP, new JspRenderer()); 
    this.getContainer().registerRenderer(Renderer.JSON, new JsonRenderer());
    this.getContainer().registerRenderer(Renderer.TEXT, new TextRenderer());
    this.getContainer().registerRenderer(Renderer.VELOCITY, new VelocityRenderer());
  }

  private void initializeFilter(String... filterClass) throws ClassNotFoundException
  {
    /*
     * 通过Webc配置文件加载过滤程序。
     */
    for (String filter : filterClass)
    {
      this.container.registerFilter((Class<WebcFilter>) this.dynamicClassLoader.loadClass(filter));
    }
    
    /*
     * 添加日志记录过滤器。
     */
    if (this.config.getParameter().containsKey(WebcConfig.CFG_LOG) && Boolean.parseBoolean(this.config.getParameter().get(WebcConfig.CFG_LOG)))
    {
      this.container.registerFilter(LogFilter.class);
    }
  }

  /**
   * 初始化Webc容器上下文，检索并缓存所有拥有{@link Handler}标注类型实现的方法。 <br/> 扫描CLASSPATH下的所有指定程序包当中的所有class文件，然后查找出所有拥有{@link Handler}标注实现的类。 <br/> <font
   * color="red">警告：如果程序包过多或者程序包类里的类数量太多可能会导致堆栈溢出，部分JVM无法释放这部分内存空间。</font>
   * <p/>
   *
   * @param basePackage 被检索的程序包列表。
   * <p/>
   * @see WebcServlet
   * @see Handler
   * @see Parameter
   * @see ResourceScaner
   * @see DynamicClassLoader
   */
  private void initializeHandler(final String... basePackage) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    List<Resource> resource;
    Class<?> scanClass;
    ResourceScaner scaner = new ResourceScaner(
            new ScanFilter()
            {
              public boolean filter(Resource resource)
              {
                // 只提取所有class文件。
                return resource.getPath().toLowerCase().endsWith(".class");
              }
            });

    /*
     * 立即向容器注册入口点，并统计扫描消耗。
     */
    int resourceClassCount = 0;
    int handlerClassCount = 0;

    this.dynamicClassLoader = new DynamicClassLoader();
    
    for (String bp : basePackage)
    {
      resource = scaner.scan(bp);
      resourceClassCount += resource.size();

      for (Resource r : resource)
      {
        try
        {
          scanClass = this.dynamicClassLoader.defineClass(ReflectUtil.getClassName(r.getPath()), r.getInputStream(), true);
        }
        catch (Throwable e)
        {
          scanClass = null;
        }

        if (scanClass != null && this.registerClass(scanClass))
        {
          handlerClassCount++;
        }
      }
    }
    
    /*
     * 添加日志控制器。
     */
    if (this.config.getParameter().containsKey(WebcConfig.CFG_LOG) && Boolean.parseBoolean(this.config.getParameter().get(WebcConfig.CFG_LOG)))
    {
      this.registerClass(LogController.class);
    }
    
    System.out.println("Scanned[" + resourceClassCount + "], Found[" + handlerClassCount + "], Handler[" + this.container.getHandlerCount() + "].");
  }
  
  public static boolean isInnerObject(Object object)
  {
    if (object instanceof LogController)
    {
      return true;
    }
    
    return false;
  }

  public WebcConfig getConfig()
  {
    return this.config;
  }

  public WebcContainer getContainer()
  {
    return this.container;
  }
}
